import pylab as p 
import click_replacement as cr
import constants as c
import utils as u
from sys import maxint

# Calcular la potencia total para cada instante de tiempo
def getPowerArray(Pxx):
    p_array = []

    for i in xrange(len(Pxx[0])):
        power = 0
        for j in xrange(len(Pxx)):
            power += (Pxx[j][i]) ** 2
        p_array.append(power)
    return p_array

def getMaxPosition(seg_array):
    max_val = -maxint
    max_pos = 0
    
    for i in xrange(len(seg_array)):
        if (seg_array[i] > max_val):
            max_pos = i
    
    return max_pos

# En base al threshold obtener los tiempos de los clicks
def getClicksFromSpectrum(power_array, threshold, bins, fs, delay):
    c_array = []
    
    for elem in power_array:
        if elem > threshold:
            pos = power_array.index(elem)
            time = bins[pos]
            # Recalcular la posicion segun el group delay del filtro FIR de fase lineal
            pos_data = time * fs - delay
            c_array.append(int(pos_data))
    return c_array

# Detectar todos los clicks dentro del segmento
def detectClicksSpectrogram(filtered_signal, fs, filter_delay):
    # Obtener el espectrograma de la salida del filtro
    # bins are the time points the spectrogram is calculated over
    # freqs is an array of frequencies
    # Pxx is a len(times) x len(freqs) array of power
    # im is a matplotlib.image.AxesImage instance
    u.Log("----Obteniendo espectro del segmento...", 0)
    Pxx, freqs, bins, im = p.specgram(filtered_signal, Fs=fs, NFFT=c.NFFT, noverlap=c.noverlap)
                    
    # Calcular la potencia media
    u.Log("----Calculando potencias...", 0)
    power_array = getPowerArray(Pxx)
        
    # Encontrar el tiempo en donde se producen los clicks
    u.Log("----Detectando clicks en espectro...", 0)
    clicks_array = getClicksFromSpectrum(power_array, c.SPEC_THRESHOLD, bins, fs, filter_delay)
    
    return clicks_array

# En base al threshold obtener los tiempos de los clicks
def getClicksFromWaveform(wave_array, threshold, fs, delay):
    c_array = []
    
    index_start = 0
    
    while (index_start < len(wave_array)):
        elem = wave_array[index_start]
        
        if (elem > threshold) or (elem < -threshold):
            # Si se supero el umbral, buscar el punto donde se vuelve a estar debajo del umbral
            index_end = index_start + 1
            while (index_end < len(wave_array)):
                elem = wave_array[index_end]
                if (elem < threshold) and (elem > -threshold):
                    break
                index_end += 1
            
            # Determinar el maximo local
            segment = wave_array[index_start : index_end]
            pos_max = getMaxPosition(segment)
            
            # Recalcular la posicion segun el group delay del filtro FIR de fase lineal
            pos_click = index_start + pos_max - delay
            c_array.append(int(pos_click))
            
            if (index_end - index_start) > c.NP_WIDTH:
                u.Log("Ventana excedida", 1)
                
            index_start = index_end
        else:
            index_start += 1
            
    return c_array

# Detectar todos los clicks dentro del segmento
def detectClicksWaveform(filtered_signal, fs, filter_delay):      
    # Encontrar el tiempo en donde se producen los clicks
    u.Log("----Detectando clicks en tiempo...", 0)
    clicks_array = getClicksFromWaveform(filtered_signal, c.WAVE_THRESHOLD, fs, filter_delay)
    
    return clicks_array

# En base al threshold obtener los tiempos de los clicks
def getClicksDynamic(wave_array, average_size, threshold, fs, delay):
    
    c_array = []
    
    index_start = 0
    old_index_start = 0
    prom_len = 0
    half_scope = average_size / 2 
    
    iteration = 0
    
    #El promedio inicia con solo la mitad del tamano por delante de la muestra actual
    if half_scope < len(wave_array):
        suma = p.sum(abs(wave_array[:half_scope]))
        prom_len = half_scope
    else:
        suma = p.sum(abs(wave_array[:]))
        prom_len = len(wave_array)
    
    prom = suma / prom_len
    
    while (index_start < len(wave_array)):
        
        iteration += 1
        
        detect_level = prom * threshold
        
        elem = wave_array[index_start]
        if (elem > detect_level) or (elem < -detect_level):
            # Si se supero el umbral, buscar el punto donde se vuelve a estar debajo del umbral
            index_end = index_start + 1
            while (index_end < len(wave_array)):
                elem = wave_array[index_end]
                if (elem < detect_level) and (elem > -detect_level):
                    break
                index_end += 1
            
            # Determinar el maximo local
            segment = wave_array[index_start : index_end]
            pos_max = getMaxPosition(segment)
            
            # Recalcular la posicion segun el group delay del filtro FIR de fase lineal
            pos_click = index_start + pos_max - delay
            c_array.append(int(pos_click))
            
            if (index_end - index_start) > c.NP_WIDTH:
                u.Log("Ventana excedida", 1)
                
            index_start = index_end
        else:
            index_start += 1
            
        if iteration % c.DYN_HOLD_TIME == 0 :
            #Modo promedio lento
            if index_start - half_scope > 0 and index_start + half_scope < len(wave_array):
                prom = p.average(abs(wave_array[index_start - half_scope : index_start + half_scope]))
            elif index_start - half_scope > 0:
                prom = p.average(abs(wave_array[index_start - half_scope :]))
            else:
                prom = p.average(abs(wave_array[: index_start + half_scope]))
            
            #Modo promedio rapido
#            if (prom_len >= average_size):
#                suma -= p.sum(abs(wave_array[old_index_start - half_scope : index_start - half_scope])) 
#                if (half_scope + index_start) < len(wave_array):
#                    suma += p.sum(abs(wave_array[old_index_start + half_scope : index_start + half_scope]))
#                else:
#                    prom_len -= index_start - old_index_start
#            else:
#                if (half_scope + index_start) < len(wave_array):
#                    suma += p.sum(abs(wave_array[old_index_start + half_scope : index_start + half_scope]))
#                    prom_len += index_start - old_index_start    
#                else:
#                    suma -= p.sum(abs(wave_array[old_index_start - half_scope : index_start - half_scope])) 
#                    prom_len -= index_start - old_index_start
#            
#            old_index_start = index_start
#            
#            prom = suma / prom_len
        
    return c_array

# Detectar todos los clicks dentro del segmento
def detectClicksDynamic(filtered_signal, fs, filter_delay):      
    # Encontrar el tiempo en donde se producen los clicks
    u.Log("----Detectando clicks dinamicamente...", 0)
    clicks_array = getClicksDynamic(filtered_signal, c.DYN_AVERAGE_SIZE , c.DYN_THRESHOLD, fs, filter_delay)
    
    return clicks_array

# Detectar todos los clicks dentro del segmento y corregirlos
def detectAndFixClicks(segment, filtered_signal, fs, filter_delay):
    # Segmentos de la senal reemplazados
    template_limits = []

    # Obtener las posiciones de los clicks
    if c.DETECT_TYPE == 'wave':
        clicks_array = detectClicksWaveform(filtered_signal, fs, filter_delay)
    elif c.DETECT_TYPE == 'spec':
        clicks_array = detectClicksSpectrogram(filtered_signal, fs, filter_delay)
    elif c.DETECT_TYPE == 'dyn':
        clicks_array = detectClicksDynamic(filtered_signal, fs, filter_delay)
        
    # Recorrer cada uno de los clicks encontrados
    for i in xrange(len(clicks_array)):
        u.Log("----Procesando click " + str(i) + " de " + str(len(clicks_array)) + "(" + str(i * 100.0 / len(clicks_array)) + "%)", 1)
        
        click = clicks_array[i]  
        
        # Determinar las ventanas a utilizar
        np_start, np_end = cr.getTemplateWindow(click, len(segment))
        prev_win_start, prev_win_end = cr.getPreviousWindow(np_start, np_end)
        post_win_start, post_win_end = cr.getPosteriorWindow(np_start, np_end, len(segment))
               
        # Obtener la plantilla original que presenta el click
        original_template = segment[np_start : np_end]
         
        # Determinar la correlacion de todas las plantillas dentro de las ventanas
        prev_corr = []
        post_corr = []
        
        if (prev_win_start >= 0 and prev_win_end <= len(segment)):
            prev_corr = cr.getCorrelationArray(segment, np_start, np_end, prev_win_start, prev_win_end)
        
        if (post_win_start >= 0 and post_win_end <= len(segment)):
            post_corr = cr.getCorrelationArray(segment, np_start, np_end, post_win_start, post_win_end)
                    
        # Obtener la plantilla de remplazo
        if (len(prev_corr) > 0 and len(post_corr) > 0):
            prev_max = max(prev_corr)
            post_max = max(post_corr)
            
            prev_template = segment[prev_win_start + prev_corr.index(prev_max) : prev_win_start + prev_corr.index(prev_max) + c.NP_WIDTH]
            post_template = segment[post_win_start + post_corr.index(post_max) : post_win_start + post_corr.index(post_max) + c.NP_WIDTH]
            
            # Obtener el promedio de las dos mejores coincidencias
            replacement_template = prev_template + post_template
            replacement_template *= 0.5
        elif (len(prev_corr) > 0 and len(post_corr) == 0):
            prev_max = max(prev_corr)
            replacement_template = segment[prev_win_start + prev_corr.index(prev_max) : prev_win_start + prev_corr.index(prev_max) + c.NP_WIDTH]
        elif (len(prev_corr) == 0 and len(post_corr) > 0):
            post_max = max(post_corr)
            replacement_template = segment[post_win_start + post_corr.index(post_max) : post_win_start + post_corr.index(post_max) + c.NP_WIDTH]
        else:
            u.Log("No hay ninguna ventana para buscar remplazos", 1)
            return template_limits
               
        template_limits.append([np_start, np_end])
                
        # Calcular el segmento a utilizar para el remplazo. Aca se usa una ventana para que sea suave
        new_segment = cr.getSmoothSegment(original_template, replacement_template)
        
        # Remplazar la seccion fallada con la nueva calculada
        segment[np_start : np_start + len(new_segment)] = new_segment 
    
    return template_limits
